// 
// File:   newmain.cc
// Author: francesco
//
// Created on June 7, 2008, 5:09 PM
//

using namespace std; 
  #include <iostream>
  #include <fstream>
  #include <cstdlib>
  #include <cstdio>
  //#include <ctype>
  #include <string>
  #include <cmath>
  #include <cfloat>

#define DEFAULT_FILE  "file.dat"
#define MAX_FEATURES  10000
#define NULL_VALUE -9876543210.0123456789

#define DEVS  2.0



  // E' un numero "magico" da attribuire alle feature non valide.

  /// E' il numero di features presenti nel features vector.
  // E' il nome del file di default per il train set.
  
 typedef struct data_item {
             double vector[MAX_FEATURES];
             int cluster;
          };
  /* E' il vettore che contiene gli items del Data Set.
  */
  
    typedef struct range {
             double avg, dev;
          };
  /* E' il vettore che contiene gli items del Data Set.
  */
struct data_item *data_set;

struct range *data_ranges;

int data_num, features_num;
 char in_file[50], out_file[50];
  /*
  ----------------- Procedura get_param -----------------------------------
  *** Scritta il: 24/02/04
  Restituisce TRUE se trova la stringa passata come terzo parametro all'in-
  terno della riga di comando passato nei primi due parametri (argc e argv).
  La stringa trovata, inoltre, viene passata copiata nell'ultimo parametro.
  *** Ultima modifica:
  */        
  bool read_param(int argc, char* argv[], char *string, char *param)
  {
     int i;
      
      
      // Si controlla se la riga di comando non contene parametri.
      if (argc == 1)
        // In tal caso si restituisce FALSE.
        return false;
        
      // Si scandisce la riga di comando alla ricerca della stringa cercata.
         for (i=1; i < argc - 1; ++i)
            if (strcmp(argv[i], string) == 0)
            {
              /* La stringa e' stata trovata, si copia il parametro succes-
                 sivo nella stringa param passata come ultima parametro. */
                 strcpy(param, argv[i + 1]);
                 
              // Si puo' restituire il valore TRUE.
              return true;   
            }    

     return false;
  }   
         


  /*
  ----------------- procedura load_data_set ------------------------------
  *** Scritta il: 19/12/03
  Carica nell'array passato come secondo parametro il data set presente nel 
  file passato come primo parametro. Il terzo parametro specifica il numero 
  di item da caricare.
  ATTENZIONE!: Questa procedura si differenzia dalla precedente dal formato 
  di memorizzazione dei dati. 
  *** Ultima modifica:
  */ 
  void load_data_set(ifstream *in_stream)
  {
     int i,j;      


     /* Ciclo principale per la memorizzazione del numero di items specifi-
        cato dal parametro items_num. */
     for (i=0; i < data_num; ++i)
     {
        // *** Inizializzazione dell'i-esimo item del data set. ***
        data_set[i].cluster = -1;
      
        for (j=0; j < features_num; j++)
	       data_set[i].vector[j]= 0.0;

        // *** Memorizzazione dei dati relativi all'i-esimo item. ***               
         
        // Memorizzazione delle features
        for (j=0; j < features_num; ++j) 
           *in_stream>>data_set[i].vector[j];    
                
        // Si memorizza il cluster vero dell'item.
        *in_stream>>data_set[i].cluster;       
     }
     
     
     return;
  }

 void write_data_set(ofstream *out_stream)
  {
     int i,j;      


     /* Ciclo principale per la memorizzazione del numero di items specifi-
        cato dal parametro items_num. */
     for (i=0; i < data_num; ++i)
     {               
        // Memorizzazione delle features
        for (j=0; j < features_num; ++j) 
           *out_stream<<data_set[i].vector[j]<<" ";    
                
        // Si memorizza il cluster vero dell'item.
        *out_stream<<data_set[i].cluster<<endl;  
            
     }
     
     
     return;
  }

  
   /*
  ----------------- procedura comp_data_ranges ---------------------------
  *** Scritta il: 19/12/03
  Calcola i ranges delle features del train set e inizializza il vettore 
  che li contiene.
  Ultima modifica:
  */
  void comp_data_ranges()
  {        
     int i, j, valids;
     double tmp_avg, tmp_dev;
     
     data_ranges = new range[features_num];
     


     for (i=0; i < features_num; ++i)                 
     {
          // Inizializzazione.
          tmp_avg = 0.0;
          tmp_dev = 0.0;
          
          // Si inizializza il contatore al numero di items validi.
          valids = 0;

          // Calcolo del valore medio dell'i-esima feature.
          for(j=0; j < data_num; ++j)
             // Si controlla se il valore di questo item e' valido. 
             if (data_set[j].vector[i] != NULL_VALUE)
             {
               tmp_avg += data_set[j].vector[i];
                
               // Si incrementa il numero di elementi validi.
               valids++;
             }   
             
          // Si verifica se c'e' almeno un elemento valido.
          if (valids)
            tmp_avg /= valids;
          
          // Si memorizza il valore medio della feature.
          data_ranges[i].avg = tmp_avg;
          
          
        // Calcolo della deviazione standard.
        for(j=0; j < data_num; ++j)
           // Si controlla se il valore e' valido.
           if (data_set[j].vector[i] != NULL_VALUE)        
             tmp_dev += pow(data_set[j].vector[i] - tmp_avg, 2.0);
        
        // Si controlla valids
        if (valids > 1)
          tmp_dev = sqrt(tmp_dev / (valids - 1));

        /* Si memorizza la deviazione nell'i-esimo elemento del vettore
           ranges. */
        data_ranges[i].dev = DEVS * tmp_dev;  

    
        /* Si controlla se la deviazione dell'i-esimo elmento e' uguale a  
           0, in tal caso la feature viene resa non valida. */
        if (data_ranges[i].dev == 0.0)
          cout<<endl<<"ATTENZIONE!: la feature: "<<i<<" ha deviazione nulla!"; 
           
     }
      
    
       
       
     return;
  }
  
   /*
  ----------------- procedura normalize -----------------------------------
  *** Scritta il: 19/12/03
  Normalizza i vettori presenti nel train set.
  Ultima modifica:
  */ 
  void normalize()
  {        
     int i, j;
     register double diff; 
     
     // *********** NORMALIZZAZIONE DEL TRAIN SET *************************  
          
     // Si normalizza l'i-esima feature.
     for (i=0; i < features_num; ++i)                       
        // Si scandisce tutto il train set.        
        for (j=0; j  < data_num; ++j)        
          // Si controlla se il valore di questa feature e' valido.
          if (data_set[j].vector[i] != NULL_VALUE)
          {            
            diff = data_set[j].vector[i] - data_ranges[i].avg;
            
            // Si controlla se il valore Ã¨ troppo distante da quello medio.
            if (diff <  data_ranges[i].dev)
              data_set[j].vector[i] = diff /  data_ranges[i].dev; 
            else if (diff > 0.0)
                   data_set[j].vector[i] = 1.0; 
                 else data_set[j].vector[i] = - 1.0;
          }
    return;
  }



int main(int argc, char** argv) {
    
    char tmp_str[50];
    int len;
    ifstream in_stream;
    ofstream out_stream;
    
    // Si legge il nome del file da normalizzare.
    read_param(argc, argv, "-in", in_file);
    
    // Si legge il nome del file di uscita.
    read_param(argc, argv, "-out", out_file);
    
    
//**** Acquisizione del numero di features
    
    // Si legge la stringa dalla riga di comando.
    if (read_param(argc, argv, "-f", tmp_str))
      // Si converte in intero
      features_num = atoi(tmp_str);
    
    //**** Acquisizione del numero di sample
    
    // Si legge la stringa dalla riga di comando.
    if (read_param(argc, argv, "-n", tmp_str))
      // Si converte in intero
      data_num = atoi(tmp_str);
   
   
    
    
    
     // Si costruisce il vettore che deve contenere i data item.     
     data_set = new data_item[data_num]; 
          
     // Si apre il file che contiene il train set
     in_stream.open(in_file);
        
     // Si controlla se il file e' stato aperto correttamente.
//      if (!in_stream) //.open(file_name))
//      {
//        cout<<"imossibile aprire il file: "<<in_file<<" per la lettura del";
//        cout<<"data set!";
//        exit(0);
//      }  
     
    // Si carica il data set dal file passato come parametro.
    load_data_set(&in_stream);

    // Si calcolano i range dei dati caricati.
     comp_data_ranges();

    // Si effettua la normalizzazione
    normalize();


     // Si apre il file che contiene il train set
     out_stream.open(out_file);
     
     // Si controlla se il file e' stato aperto correttamente.
     if (!out_stream) //.open(file_name))
     {
       cout<<"imossibile aprire il file: "<<out_file<<" per salvare i dati";       
       exit(0);
     }  

     write_data_set(&out_stream);

   

    

    
      
cout<<endl<<"normalizzazione eseguita:";
cout<<endl<<"file di input:"<<in_file;
cout<<endl<<"numero di feature:"<<features_num;
cout<<endl<<"numero di samples:"<<data_num;
cout<<endl<<"file di output:"<<out_file<<endl;



    
    return (EXIT_SUCCESS);
}

